<?php
namespace Quality\Start\App\Service;

use \Symfony\Component\HttpFoundation\Session\Session;
use \Symfony\Component\HttpKernel\Event\GetResponseEvent;
use \Symfony\Component\Security\Http\Firewall\ListenerInterface;
use \DateTime;
use \Quality\Start\Domain\Entity\User;
use \Quality\Start\Domain\Repository\UserRepository;
use \Symfony\Component\HttpFoundation\Request;

class UserControl implements ListenerInterface
{
    /**
     * @var \Symfony\Component\HttpFoundation\Session\Session
     */
    private $session;

    /**
     * @var \Quality\Start\Domain\Repository\UserRepository
     */
    private $userRepository;

    /**
     * @var \Quality\Start\Domain\Entity\User
     */
    private $user;

    /**
     * @var string
     */
    const USER_SESSION_NAME = "QualityLoggedUser";

    /**
     * @param \Quality\Start\Domain\Repository\UserRepository $userRepository
     * @param \Symfony\Component\HttpFoundation\Session\Session $session
     */
    public function __construct(UserRepository $userRepository, Session $session)
    {
        $this->userRepository = $userRepository;
        $this->session = $session;
        $this->session->start();
    }

    /**
     * Clear all attributes
     */
    private function clearSession()
    {
        if ($this->session->has(self::USER_SESSION_NAME)) {
            $this->session->remove(self::USER_SESSION_NAME);
        }

        $this->session->clear();
    }

    /**
     * @param string $login
     * @return bool
     */
    private function userExist($username)
    {
        $user = $this->userRepository->findByLogin($username);

        if (!is_null($user)) {
            $this->user = $user;
            return true;
        }

        return false;
    }

    /**
     * @param string $login
     * @param string $pass
     * @return bool
     */
    private function isValidUser($username, $password)
    {
        return !$this->userExist($username) ?: HashGenerator::check($password, $this->user->getPass());
    }

    /**
     * @param string $login
     * @param string $pass
     * @return bool
     */
    public function login($username, $password)
    {
        $this->clearSession();

        if ($this->isValidUser($username, $password)) {
            $this->session->set(self::USER_SESSION_NAME, serialize($this->user));
            $this->user->setLastAccess(new DateTime());
            $this->userRepository->update($this->user);
            return true;
        }

        return false;
    }

    public function logout()
    {
        $this->clearSession();
    }

    /**
     * @return bool|\Quality\Start\Domain\Entity\User
     */
    public function getLoggedUser()
    {
        if ($this->session->has(self::USER_SESSION_NAME)) {
           return unserialize($this->session->get(self::USER_SESSION_NAME));
        }

        return false;
    }

    /**
     * This interface must be implemented by firewall listeners.
     * @param GetResponseEvent $event
     */
    public function handle(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        $wsseRegex = '/UsernameToken Username="([^"]+)", PasswordDigest="([^"]+)", Nonce="([^"]+)", Created="([^"]+)"/';
        if (!$request->headers->has('x-wsse') || 1 !== preg_match($wsseRegex, $request->headers->get('x-wsse'), $matches)) {
            return;
        }

        $token = new WsseUserToken();
        $token->setUser($matches[1]);
        $token->digest = $matches[2];
        $token->nonce = $matches[3];
        $token->created = $matches[4];

        try {
            $authToken = $this->authenticationManager->authenticate($token);

            $this->securityContext->setToken($authToken);
        } catch (AuthenticationException $failed) {
            // ... you might log something here

            // To deny the authentication clear the token. This will redirect to the login page.
            // return;

            // Deny authentication with a '403 Forbidden' HTTP response
            // $this->securityContext->setToken(null);
            $response = new Response();
            $response->setStatusCode(403);
            $event->setResponse($response);

        }
    }
}